package astar;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Vector;

import domain.Agent;
import domain.AgentSmart;
import domain.Board;
import domain.Debug;
import domain.Flag;
import domain.Game;
import domain.Game.action;

/**
 *
 * @author Shai Givony
 */
public class AStar {

    private AgentSmart				_agentMe;
    private PriorityQueue<ANode>    _priorityQ;
    private Set<ANode> 				_history;
    private Vector<ANode>			_history2;
    private int 					_counter;
    
    public AStar(AgentSmart agentMe) {
        
    	this._agentMe 	= agentMe;
        this._priorityQ = new PriorityQueue<ANode>();
        this._counter 	= 300000;							// real time A*
        this._history 	= new HashSet<ANode>();			// to avoid states duplication
        this._history2	= new Vector<ANode>();
    }
    
    public LinkedList<action> runAStarSearch(Board board, Vector<Agent> agents, Vector<Flag> flags){
        LinkedList<action> ans = new LinkedList<action>();
        
        //constructing first node of A* tree:
        ANode root = new ANode(Game._board, this._agentMe, agents, flags, this._agentMe.getScore(), ans);
        _priorityQ.add(root);
        ANode nodeWithMinimalF = _priorityQ.poll();		//extract with minimal f function
        
        while(!endCondition(nodeWithMinimalF)){
        	
            Debug.println("node with minimal f: " + nodeWithMinimalF);
            if(!stateExist(nodeWithMinimalF)){//preventing duplicated states.
            	
            	//this._history.add(nodeWithMinimalF);// adding the ANode to the history.
	            this._history2.addElement(nodeWithMinimalF);
            	
            	//Extract:
            	Debug.println("\n-------------------------------------\nSimulate all actions from state "+nodeWithMinimalF.getPath()+":\n-------------------------------------");
	            ANode after_up      = nodeWithMinimalF.simulateNode(action.UP);
	            ANode after_right   = nodeWithMinimalF.simulateNode(action.RIGHT);
	            ANode after_down    = nodeWithMinimalF.simulateNode(action.DOWN);
	            ANode after_left    = nodeWithMinimalF.simulateNode(action.LEFT);
	            Debug.println("Simulatation finished.\n-------------------------------------\n");
	            
	            Debug.println("Sentry at: " + after_up.getAgents().elementAt(0).getSquare());
	            Debug.println(after_up + "\n" + after_right + "\n" + after_down + "\n" + after_left + "\n");
	            
	            addNodeToQueue(after_up);
	            addNodeToQueue(after_right);
	            addNodeToQueue(after_down);
	            addNodeToQueue(after_left);
            }
            nodeWithMinimalF = _priorityQ.poll();		//extract with minimal f function
        }
        if (nodeWithMinimalF != null)
        	ans = nodeWithMinimalF.getPath();
        System.out.println("I want to go: " + ans);
        //ans = _priorityQ.peek().getPath();
        return ans;
    }

	private void addNodeToQueue(ANode node) {
		if(node!=null){
		    _priorityQ.add(node);
		}
	}

    private boolean stateExist(ANode node) {
    	for (Iterator<ANode> iterator = this._history2.iterator(); iterator.hasNext();) {
			ANode node_i = iterator.next();
			if (node_i.equals(node)){
				Debug.println("Exists");
				return true;
			}
		}
    	return false;
    	//boolean ans = this._history.contains(node);
//    	if (ans)
//    		Debug.println("C O N T A I N S");
//		return ans;
	}

	private boolean endCondition(ANode nodeWithMinimalF) {
        this._counter--;
		return nodeWithMinimalF == null || nodeWithMinimalF.isGoalNode() || this._counter == 0;
    }
    
    
}
